package orm

import (
	"api/public/global"
	"context"
	"fmt"
	"gitee.com/dreamwood/ez"
	"gorm.io/gorm"
	"reflect"
	"strings"
)

type BaseModel struct {
	ctx   context.Context
	timer *ez.Timer
	db    *gorm.DB
	//事件触发相关
	isEventOff bool
	pkgName    string
	modelName  string

	//扫描缓存
	scanPool *ScanPool
}

func (this *BaseModel) SetScanPool(scanPool *ScanPool) {
	//ez.JsonLog(scanPool)
	if scanPool == nil {
		println("scanPool is nil========================")
	}
	this.scanPool = scanPool
}

func (this *BaseModel) GetScanPool() *ScanPool {
	if this.scanPool == nil {
		this.scanPool = NewScanPool()
	}
	return this.scanPool
}

func (this *BaseModel) DB() *gorm.DB {
	if this.db == nil {
		this.db = global.DB
	}
	return this.db
}

func (this *BaseModel) SetDb(tx *gorm.DB) {
	this.db = tx
}
func (this *BaseModel) SetTimer(timer *ez.Timer) {
	this.timer = timer
}
func (this *BaseModel) GetTimer() *ez.Timer {
	return this.timer
}
func (this *BaseModel) SelectIdOnly(db *gorm.DB) *gorm.DB {
	return db.Select("id")
}

// 事件监听相关
func (this *BaseModel) GetIsEventOn() bool {
	return this.isEventOff
}
func (this *BaseModel) SetEventOn() *BaseModel {
	this.isEventOff = false
	return this
}
func (this *BaseModel) SetEventOff() *BaseModel {
	this.isEventOff = true
	return this
}
func (this *BaseModel) Trigger(eventName string, data interface{}) *BaseModel {
	if !this.isEventOff {
		ez.DispatchToMany(eventName, data, this.GetCtx())
	}
	return this
}
func (this *BaseModel) SetPkgName(name string) *BaseModel {
	this.pkgName = name
	return this
}
func (this *BaseModel) SetModelName(name string) *BaseModel {
	this.modelName = name
	return this
}
func (this *BaseModel) EventTriggerNew(data interface{}) *BaseModel {
	this.Trigger(fmt.Sprintf("%s.%sNew", this.pkgName, this.modelName), data)
	return this
}
func (this *BaseModel) EventTriggerBeforeCreate(data interface{}) *BaseModel {
	this.Trigger(fmt.Sprintf("%s.Before%sCreate", this.pkgName, this.modelName), data)
	return this
}
func (this *BaseModel) EventTriggerBeforeUpdate(data interface{}) *BaseModel {
	this.Trigger(fmt.Sprintf("%s.Before%sUpdate", this.pkgName, this.modelName), data)
	return this
}
func (this *BaseModel) EventTriggerAfterCreate(data interface{}) *BaseModel {
	this.Trigger(fmt.Sprintf("%s.After%sCreate", this.pkgName, this.modelName), data)
	return this
}
func (this *BaseModel) EventTriggerAfterUpdate(data interface{}) *BaseModel {
	this.Trigger(fmt.Sprintf("%s.After%sUpdate", this.pkgName, this.modelName), data)
	return this
}
func (this *BaseModel) EventTriggerDelete(data interface{}) *BaseModel {
	this.Trigger(fmt.Sprintf("%s.%sDelete", this.pkgName, this.modelName), data)
	return this
}

func (this BaseModel) ValueOf(model interface{}, fields string) reflect.Value {
	strSlice := strings.Split(fields, ".")
	var value reflect.Value
	value = reflect.ValueOf(model)
	for index, field := range strSlice {
		if index == 0 {
			value = value.FieldByName(ez.Ucfirst(field))
		} else {
			value = value.Elem().FieldByName(ez.Ucfirst(field))
		}
	}
	return value
}

func (this *BaseModel) SetCtx(ctx context.Context) *BaseModel {
	this.ctx = ctx
	return this
}
func (this *BaseModel) GetCtx() context.Context {
	return this.ctx
}

type BaseCrud struct {
	Associations []string
	db           *gorm.DB
	SafeDelete   bool
}

func (this *BaseCrud) DB() *gorm.DB {
	if this.db == nil {
		this.db = global.DB
	}
	return this.db
}

func (this *BaseCrud) SetDb(tx *gorm.DB) {
	this.db = tx
}
func (this *BaseCrud) GetAssociation() []string {
	if this.Associations == nil {
		return nil
	}
	return this.Associations
}
func (this *BaseCrud) SetAssociation(field ...string) []string {
	this.Associations = field
	return this.Associations
}

type EzModelConfig struct {
	ContainerKey string
	TableName    string
	TablePrefix  string
	TableSuffix  string
	EventKeys    map[string]string
	Fields       []string
	Relations    map[string]*EzModelRelation
	Crud         map[string]EzModelFieldLimitation
}

/*
*
返回完整的数据表名
*/
func (this *EzModelConfig) GetTableName() string {
	return this.TablePrefix + this.TableName + this.TableSuffix
}

/*
*
获取Model下某个关联的配置信息
*/
func (this *EzModelConfig) GetRelation(name string) *EzModelRelation {
	return this.Relations[name]
}

/*
*
获取Curd配置
*/
func (this *EzModelConfig) GetCurdConfig(confKeys ...string) EzModelFieldLimitation {
	configKey := "default"
	if len(confKeys) > 0 {
		configKey = confKeys[0]
	}
	find, ok := this.Crud[configKey]
	if ok {
		return find
	} else {
		return EzModelFieldLimitation{
			Select:    []string{},
			Omit:      []string{},
			Relations: []string{},
		}
	}
}

//func (this *EzModelConfig) GetCurdUpdate(confKeys ...string) EzModelFieldLimitation {
//	configKey := "default"
//	if len(confKeys) > 0 {
//		configKey = confKeys[0]
//	}
//	return this.Crud.GetUpdate(configKey)
//}
//func (this *EzModelConfig) GetCurdSelect(confKeys ...string) EzModelFieldLimitation {
//	configKey := "default"
//	if len(confKeys) > 0 {
//		configKey = confKeys[0]
//	}
//	config := this.Crud.GetSelect(configKey)
//	if len(config.Select) == 0 {
//		config.Select = this.Fields
//	}
//	return config
//}
//func (this *EzModelConfig) GetCurdList(confKeys ...string) EzModelFieldLimitation {
//	configKey := "default"
//	if len(confKeys) > 0 {
//		configKey = confKeys[0]
//	}
//	config := this.Crud.GetList(configKey)
//	if len(config.Select) == 0 {
//		config.Select = this.Fields
//	}
//	return config
//}
//func (this *EzModelConfig) GetCurdScan(confKeys ...string) EzModelFieldLimitation {
//	configKey := "default"
//	if len(confKeys) > 0 {
//		configKey = confKeys[0]
//	}
//	config := this.Crud.GetScan(configKey)
//	//if len(config.Select) == 0 {
//	//	config.Select = this.Fields
//	//}
//	return config
//}

/*
*
关联项的配置内容
*/
type EzModelRelation struct {
	Config       func() *EzModelConfig
	ContainerKey string ""
	JoinType     string //M2O,O2M,M2M
	FieldLeft    string
	FieldRight   string
	//多对多
	LinkedTable string //中间表
}

/*
*
返回Join查询时用于联表的核心语句
*/
func (this *EzModelRelation) CreateJoinOn(from string, to string) []string {
	switch this.JoinType {
	case "M2O", "O2M", "O2O":
		return []string{
			fmt.Sprintf("left join %s %s on %s.%s = %s.%s",
				this.Config().GetTableName(),
				to,
				from,
				this.FieldLeft,
				to,
				this.FieldRight),
		}
	case "M2M":
		return []string{
			fmt.Sprintf("left join %s on %s.id = %s.%s",
				this.LinkedTable,
				from, this.LinkedTable, this.FieldLeft),
			fmt.Sprintf("left join %s %s on %s.id = %s.%s",
				this.Config().GetTableName(), to,
				to,
				this.LinkedTable, this.FieldRight),
		}
	default:
		return nil
	}
}

/*
*
Model增删改查是的字段限制
*/
type EzModelFieldLimitation struct {
	Select    []string
	Omit      []string
	Relations []string
}

func (this EzModelFieldLimitation) GetFields() string {
	tmp := make(map[string]bool)
	for _, f := range this.Select {
		tmp[ez.Ucfirst(f)] = true
	}
	for _, f := range this.Omit {
		tmp[ez.Ucfirst(f)] = false
	}
	arr := make([]string, 0)
	for f, has := range tmp {
		if has {
			arr = append(arr, ez.Ucfirst(f))
		}
	}
	return strings.Join(arr, " ")
}

/*
*
Model增删改查的相关配置
*/
//type EzModelCrudConfig struct {
//	Create map[string]EzModelFieldLimitation
//	Update map[string]EzModelFieldLimitation
//	Select map[string]EzModelFieldLimitation
//	List   map[string]EzModelFieldLimitation
//	Scan   map[string]EzModelFieldLimitation
//}
//
//func (this *EzModelCrudConfig) GetCreate(confKey string) EzModelFieldLimitation {
//	if confKey == "" {
//		confKey = "default"
//	}
//	if this.Create != nil {
//		defaultConf, ok := this.Create[confKey]
//		if ok {
//			return defaultConf
//		}
//	}
//	return EzModelFieldLimitation{
//		Select:    []string{},
//		Omit:      []string{},
//		Relations: []string{},
//	}
//}
//func (this *EzModelCrudConfig) GetUpdate(confKey string) EzModelFieldLimitation {
//	if confKey == "" {
//		confKey = "default"
//	}
//	if this.Update != nil {
//		defaultConf, ok := this.Update[confKey]
//		if ok {
//			return defaultConf
//		}
//	}
//	return EzModelFieldLimitation{
//		Select:    []string{},
//		Omit:      []string{},
//		Relations: []string{},
//	}
//}
//
//func (this *EzModelCrudConfig) GetSelect(confKey string) EzModelFieldLimitation {
//	if confKey == "" {
//		confKey = "default"
//	}
//	if this.Select != nil {
//		defaultConf, ok := this.Select[confKey]
//		if ok {
//			return defaultConf
//		}
//	}
//	return EzModelFieldLimitation{
//		Select:    []string{},
//		Omit:      []string{},
//		Relations: []string{},
//	}
//}
//func (this *EzModelCrudConfig) GetList(confKey string) EzModelFieldLimitation {
//	if confKey == "" {
//		confKey = "default"
//	}
//	if this.List != nil {
//		defaultConf, ok := this.List[confKey]
//		if ok {
//			return defaultConf
//		}
//	}
//	return EzModelFieldLimitation{
//		Select:    []string{},
//		Omit:      []string{},
//		Relations: []string{},
//	}
//}
//func (this *EzModelCrudConfig) GetScan(confKey string) EzModelFieldLimitation {
//	if confKey == "" {
//		confKey = "default"
//	}
//	if this.Scan != nil {
//		defaultConf, ok := this.Scan[confKey]
//		if ok {
//			return defaultConf
//		}
//	}
//	return EzModelFieldLimitation{
//		Select:    []string{},
//		Omit:      []string{},
//		Relations: []string{},
//	}
//}
